/*
 *    Copyright © OpenAtom Foundation.
 *
 *    Licensed under the Apache License, Version 2.0 (the "License");
 *    you may not use this file except in compliance with the License.
 *    You may obtain a copy of the License at
 *
 *         http://www.apache.org/licenses/LICENSE-2.0
 *
 *    Unless required by applicable law or agreed to in writing, software
 *    distributed under the License is distributed on an "AS IS" BASIS,
 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *    See the License for the specific language governing permissions and
 *    limitations under the License.
 */

package com.inspur.edp.bef.core.be;

import com.inspur.edp.bef.api.action.determination.BeDeterminationTypes;
import com.inspur.edp.bef.api.be.IBENodeEntity;
import com.inspur.edp.bef.api.be.IBusinessEntity;
import com.inspur.edp.bef.api.parameter.clone.CloneParameter;
import com.inspur.edp.bef.api.parameter.retrieve.RespectiveRetrieveResult;
import com.inspur.edp.bef.api.parameter.retrieve.RetrieveChildResult;
import com.inspur.edp.bef.api.parameter.retrieve.RetrieveParam;
import com.inspur.edp.bef.core.DotNetToJavaStringHelper;
import com.inspur.edp.bef.core.action.base.ActionFactory;
import com.inspur.edp.bef.core.action.modify.FullModifyAction;
import com.inspur.edp.bef.core.action.modify.ModifyAction;
import com.inspur.edp.bef.core.action.retrieve.EditAction;
import com.inspur.edp.bef.core.action.retrieve.RetrieveAction;
import com.inspur.edp.bef.core.action.retrieve.RetrieveChildAction;
import com.inspur.edp.bef.core.action.retrieve.RetrieveChildAndChildAction;
import com.inspur.edp.bef.core.action.retrieve.RetrieveWithScopeAction;
import com.inspur.edp.bef.core.action.retrievedefault.FullRetrieveDefaultAction;
import com.inspur.edp.bef.core.action.save.SaveContext;
import com.inspur.edp.bef.core.action.save.SaveScopeNodeParameter;
import com.inspur.edp.bef.core.coderule.RootCodeRuleCompensater;
import com.inspur.edp.bef.core.buffer.UndoChangeDetailMerger;
import com.inspur.edp.bef.core.determination.AfterQueryDtmExecutor;
import com.inspur.edp.bef.core.determination.BefB4SaveAdditionalDtmExecutor;
import com.inspur.edp.bef.core.determination.BefB4SaveDtmExecutor;
import com.inspur.edp.bef.core.determination.RootEntityAfterModifyDtmExecutor;
import com.inspur.edp.bef.core.determination.builtinimpls.BefAfterQueryDtmAssembler;
import com.inspur.edp.bef.core.determination.builtinimpls.BefB4SaveDtmAssembler;
import com.inspur.edp.bef.spi.entity.CodeRuleInfo;
import com.inspur.edp.bef.spi.entity.builtinimpls.BefEntityResInfoImpl;
import com.inspur.edp.cdp.coderule.api.CodeRuleBEAssignService;
import com.inspur.edp.cef.api.action.EditResult;
import com.inspur.edp.cef.entity.changeset.ChangeType;
import com.inspur.edp.cef.entity.changeset.IChangeDetail;
import com.inspur.edp.cef.entity.changeset.ModifyChangeDetail;
import com.inspur.edp.cef.entity.entity.IEntityData;
import com.inspur.edp.cef.entity.entity.IEntityDataCollection;
import com.inspur.edp.cef.spi.determination.IEntityRTDtmAssembler;

import com.inspur.edp.cef.spi.extend.entity.ICefEntityExtend;
import com.inspur.edp.cef.spi.entity.info.propertyinfo.DataTypePropertyInfo;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import lombok.var;
import org.springframework.util.StringUtils;
import sun.reflect.generics.reflectiveObjects.NotImplementedException;

public abstract class BusinessEntity extends BENodeEntity implements IBusinessEntity {

	//region Constructor
	protected BusinessEntity(String dataId) {
		super(dataId);
	}

	//endregion


	//region 属性字段
//TODO: java版临时注释 无internal修饰符, 改成public
	public abstract String getNodeCode();

	@Override
	public boolean isRoot() {
		return true;
	}

	private String privateBEType;

	@Override
	public final String getBEType() {
		return privateBEType;
	}

	public final void setBEType(String value) {
		privateBEType = value;
	}

//	private IBEContext IBusinessEntity.BEContext => (IBEContext)Context;


	@Override
	public CoreBEContext getBEContext() {
		return (CoreBEContext) super.getContext();
	}


	/// <summary>
	/// 赋值版本信息
	/// </summary>
	protected String getVersionControlPropName() {
		return "";
	}

	@Override
	public final String getVersionControlPropertyName() {
		if(getVersion()==0)
		return getVersionControlPropName();
	return	((BefEntityResInfoImpl)getEntityResInfo()).getVersionControlPropertyName();
	}

	@Override
	public Date getVersionControlPropValue() {
		String propName = getVersionControlPropertyName();
		if (DotNetToJavaStringHelper.isNullOrEmpty(propName)) {
			return new Date(0);
		}
		return (Date) getContext().getData().getValue(propName);
	}

	@Override
	public void setVersionControlPropValue(Date value) {
		if(getBEContext().getVersionSetted() || isEmptyChange(getContext().getCurrentTransactionalChange()))
			return;
		String propName = getVersionControlPropertyName();
		if (!StringUtils.isEmpty(propName)) {
			if (getContext().getData() != null) {
				getContext().getData().setValue(propName, value);
				getBEContext().setVersionSetted(true);
				getBEContext().mergeIntoInnerChange();
				getBEContext().acceptListenerChange();

			}
		}
	}

	@Override
    public void assignDefaultValue(IEntityData data) {
        for(Map.Entry<String, DataTypePropertyInfo> entry:getEntityResInfo().getEntityTypeInfo().getPropertyInfos().entrySet()){
            if(entry.getValue().getDefaultValue() != null){
                switch (entry.getValue().getDefaultValue().getValueType()){
                    case Value:
                        data.setValue(entry.getValue().getPropertyName(), entry.getValue().getDefaultValue().getRealValue());
                        break;
                    case Expression:
                        data.setValue(entry.getValue().getPropertyName(), entry.getValue().getDefaultValue().getRealValue());
                        break;
                }
            }
        }
    }
	private  boolean isEmptyChange(IChangeDetail changeDetail) {
		if (changeDetail == null)
			return true;
		switch (changeDetail.getChangeType()) {
			case Added:
			case AddedOrModify:
			case Deleted:
				return false;
			case Modify:
				ModifyChangeDetail modifyChangeDetail=(ModifyChangeDetail)changeDetail;
				if(modifyChangeDetail.getPropertyChanges()!=null&&modifyChangeDetail.getPropertyChanges().size()>0)
					return false;
				if(modifyChangeDetail.getChildChanges()!=null&&modifyChangeDetail.getChildChanges().size()>0)
					return false;
				return true;
				default:
					throw new RuntimeException("未识别的变更类型"+changeDetail.getChangeType());
		}
	}
	//endregion


	//region Retrieve
@Override
	public IEntityData retrieveData() {
		return retrieve().getData();
	}

	@Override
	public final RespectiveRetrieveResult retrieve() {
		RetrieveParam tempVar = new RetrieveParam();
		return retrieve(tempVar);
	}

	@Override
	public final RespectiveRetrieveResult retrieve(RetrieveParam para) {
		ActionFactory actionFactory = getActionFactory();
		RetrieveAction retrieveAction = actionFactory.getRetrieveAction(getBEContext(), para);
		return execute(retrieveAction);
	}

	@Override
	public final EditResult edit() {
		ActionFactory actionFactory = getActionFactory();
		EditAction retrieveAction = actionFactory.getEditAction(getBEContext());
		return execute(retrieveAction);
	}

	@Override
	public final RespectiveRetrieveResult retrieveChild(java.util.List<String> nodeCodes, java.util.List<String> hierachyIdList, RetrieveParam par) {
		ActionFactory actionFactory = getActionFactory();
		RetrieveChildAction retrieveAction = actionFactory.getRetrieveChildAction(nodeCodes, hierachyIdList, par);
		return execute(retrieveAction);
	}

	@Override
	public final RetrieveChildResult retrieveChild(java.util.List<String> nodeCodes, java.util.List<String> hierachyIdList, ArrayList<String> ids, RetrieveParam par) {
		ActionFactory actionFactory = getActionFactory();
		RetrieveChildAndChildAction retrieveAction = actionFactory.getRetrieveChildAndChildAction(nodeCodes, hierachyIdList, ids,par);
		return execute(retrieveAction);
	}
	//region LoadData
	public final void loadData(IEntityData data) {
		getBEContext().setOriginalData((IEntityData) getBEContext().getBufferChangeManager().initBuffer_ZeroLevel(data, true)); //original
		getBEContext().setTransactionData((IEntityData) getBEContext().getBufferChangeManager().initTransactionBuffer(data.getID())); //transaction
		getBEContext().setCurrentData((IEntityData) getBEContext().getBufferChangeManager().initCurrentBuffer(data.getID())); //current
		getBEContext().startChangeListener();
	}

	public final void appendTempCurrentChange(IChangeDetail changeDetail) {
		getBEContext().getBufferChangeManager().appendCurrentTemplateChange(changeDetail);
		//BEContext.ChangesetManager.appendCurrentChange(changeDetail);

		getBEContext().suspendChangeListener();
		try {
			if (changeDetail != null && changeDetail.getChangeType() != ChangeType.Added) {
				getBEContext()
						.setCurrentData(
								(IEntityData)
										getBEContext()
												.getBufferChangeManager()
												.acceptTemplateCurrentChange(getID(), changeDetail));
			}
			//switch (changeDetail.getChangeType())
			//{
			//    case ChangeType.Modify:
			//        ((IAccessor)BEContext.CurrentData).acceptChange(changeDetail);
			//        break;
			//    case ChangeType.Deleted:
			//       BEContext.CurrentData = null;
			//        break;
			//    case ChangeType.Added:
			//        throw new NotImplementedException();
			//    default:
			//        throw new BefEngineException(ErrorCodes.UnknownChangeType, changeDetail.getChangeType().toString());
			//}
		} finally {
			getBEContext().resumeChangeListener();
		}
	}

	//public void createEntityData()
	//{
	//    if (BEContext.hasData())
	//        throw new BefException(ErrorCodes.InvalidRecreation, "不能重复创建数据");

	//    var ed = CreateEntityData(ID);
	//    //BEContext.BufferManager.initBuffer_ZeroLevel(ed, true);
	//    var currData = (IEntityData)BEContext.BufferManager.initCurrentBuffer(ed);
	//    BEContext.CurrentData = currData;
	//    BEContext.beginChangeListener();

	//    //CurrentChange = ChangeFromModify = new EntityAddChangeDetail((IAccessor)CurrentData);
	//    BEContext.appendTempCurrentChange(new AddChangeDetail(currData));
	//}

	//endregion

	//endregion

	@Override
	public final void retrieveWithScope(RetrieveParam para) {
		ActionFactory actionFactory = getActionFactory();
		RetrieveWithScopeAction retrieveWithScopeAction = actionFactory.getRetrieveWithScopeAction(getBEContext(), para);
		execute(retrieveWithScopeAction);
		//return retrieveWithScopeAction.RetrieveResult;
	}

	@Override
	public void modify(IChangeDetail changeDetail) {
		ActionFactory actionFactory = getActionFactory();
		ModifyAction modifyAction = actionFactory.getModifyAction(getBEContext(), changeDetail);
		execute(modifyAction);
	}

	public void modify(IEntityData data) {
		execute(new FullModifyAction(getBEContext(), data));
	}


	@Override
	public final boolean save() {
		return execute(getActionFactory().getSaveAction());
	}
	public final void generateMainCode(SaveContext ctx, IEntityData data, HashMap<String,Object> map){
			java.util.HashMap<String, CodeRuleInfo> codeInfo = getBeforeSaveCodeRule();
			if (codeInfo == null || codeInfo.isEmpty()) {
				return;
			}
			for (var key : codeInfo.keySet()) {

				var currentCodeValue = data.getValue(key);
				String stringValue = (currentCodeValue instanceof String) ? (String) currentCodeValue : null;
				if (currentCodeValue != null && stringValue != null && !stringValue.equals("")) {
					continue;
				}

				CodeRuleBEAssignService service = getCodeRuleInstance();
				String code = service.generate(
						getBEContext().getBEManagerContext().getBEManager().getBEInfo().getBEID(),
						getNodeCode(),
						key,
						map,
						codeInfo.get(key).getCodeRuleId());
				ctx.addCompensater(new RootCodeRuleCompensater(this, key));
				//主表编号规则
				data.setValue(key, code);
			}
	}
	public  void beforeSaveCodeGenerate(SaveContext ctx) {
		if (getBEContext().getCurrentTransactionalChange() == null || getBEContext().getCurrentTransactionalChange().getChangeType()==ChangeType.Deleted) {
			return;
		}
		var data = getContext().getCurrentData();
		HashMap<String,Object> map=new HashMap<>(4);
		map.put(getNodeCode(),data);
		//主表子表编号
		generateMainCode(ctx, data, map);
		//子表编号规则
		assignCode(ctx, data,map,this);
		getBEContext().mergeIntoInnerChange();
		getBEContext().acceptListenerChange();
		getBEContext().acceptTempChange();
	}

	public final void assignCode(SaveContext ctx, IEntityData data, HashMap<String, Object> map,
			IBENodeEntity entity) {
		if (!getBEContext().hasChange() || getBEContext().isDeleted() || data.getChilds().isEmpty()) {
			return;
		}

		for (Map.Entry<String, IEntityDataCollection> childCollection : data.getChilds().entrySet()) {
			if (childCollection.getValue().isEmpty()) {
				continue;
			}
			for (IEntityData childData : childCollection.getValue()) {
				//子表实体
				BENodeEntity childEntity = (BENodeEntity) entity
						.getChildBEEntity(childCollection.getKey(), childData.getID());
				childEntity.beforeSaveCodeGenerate(ctx, map,
						getBEContext().getBEManagerContext().getBEManager().getBEInfo().getBEID());
			}

		}
	}

	public final void saveWithScope() {
		getBEContext().addScopeParameter(new SaveScopeNodeParameter(getBEContext()));
	}

	@Override
	public final void retrieveDefault() {
		retrieveDefault(Collections.emptyMap());
	}

	@Override
	public final void retrieveDefault(java.util.Map<String, Object> defaultValues) {
		//  DataValidator.checkForNullReference(defaultValues, "defaultValues");
			execute(getActionFactory().getRetrieveDefaultAction(defaultValues));
	}

	public final void retrieveDefault(IEntityData data) {
		ActionFactory actionFactory = getActionFactory();
		FullRetrieveDefaultAction retrieveAction = actionFactory.getFullRetrieveDefaultAction(getBEContext(), data);
		execute(retrieveAction);
	}

	@Override
	public final void clone(String cloneDataID, CloneParameter cloneParameter) {
		clone(cloneDataID, "", cloneParameter);
	}

	@Override
	public final void clone(String cloneDataID, String dataId, CloneParameter cloneParameter) {
		execute(getActionFactory().getCloneAction(cloneDataID, dataId, cloneParameter));
	}

	@Override
	public final IEntityData retrieveDefaultChild(java.util.List<String> nodeCodes, java.util.List<String> hierachyIdList) {
		return execute((getActionFactory().getRetrieveDefaultChildAction(nodeCodes, hierachyIdList, null, null)));
	}

	@Override
	public final IEntityData retrieveDefaultChild(java.util.List<String> nodeCodes, java.util.List<String> hierachyIdList, String dataID) {
		return execute((getActionFactory().getRetrieveDefaultChildAction(nodeCodes, hierachyIdList, dataID, null)));
	}

	public final IEntityData retrieveDefaultChild(java.util.List<String> nodeCodes, java.util.List<String> hierachyIdList, String dataID, java.util.Map<String, Object> values) {
		return execute((getActionFactory().getRetrieveDefaultChildAction(nodeCodes, hierachyIdList, dataID, values)));
	}

	@Override
	public final IEntityData retrieveDefaultChild(java.util.List<String> nodeCodes, java.util.List<String> hierachyIdList, java.util.Map<String, Object> values) {
		return execute((getActionFactory().getRetrieveDefaultChildAction(nodeCodes, hierachyIdList, null, values)));
	}

	@Override
	public final void delete() {
		execute(getActionFactory().getDeleteAction());
	}

	@Override
	public final void deleteChild(java.util.List<String> nodeCodes, java.util.List<String> hierachyIdList, java.util.List<String> ids) {
		execute(getActionFactory().getDeleteChildAction(nodeCodes, hierachyIdList, ids));
	}

	@Override
	public final void deleteWithScope() {
		throw new NotImplementedException();
	}

	@Override
	public final void addLockWithScope() {
		execute(getActionFactory().getAddLockAction());
	}

	/**
	 * 获取ActionFactory，以获取RetrieveAction,AddLockAction等
	 *
	 * @return
	 */
	private ActionFactory getActionFactory() {
		return new ActionFactory();
	}

	@Override
	public final void clearChangeset() {
		execute(getActionFactory().getClearChangesetAction());
	}

	@Override
	public final void clearBuffer() {
		execute(getActionFactory().getClearBufferAction());
	}

	@Override
	public final IEntityData createObjectData() {
		return executeNoSession(getActionFactory().getCreateObjectDataAction(getBEContext()));
	}

	//ORIGINAL LINE: public IEntityData createChildData(string childCode, string dataID = "")
	@Override
	public final IEntityData createChildData(String childCode, String dataID) {
		return executeNoSession(getActionFactory().getCreateChildDataAction(getBEContext(), childCode, dataID));
	}


	//region Dtm/Validation
	@Override
	public void afterModifyDeterminate() {

		IEntityRTDtmAssembler ass = getAfterModifyDtmAssembler();
		if (ass == null) {
			return;
		}

		if(extList != null){
			for(ICefEntityExtend ext:extList){
				ext.addExtDtm(ass, BeDeterminationTypes.AfterModify);
			}
		}

		checkEntityContext();
		new RootEntityAfterModifyDtmExecutor(ass, getBEContext()).execute();
	}

	@Override
	public void beforeSaveDeterminate() {
		if (getBEContext().getBeforeSaveDeterminated()) {
			throw new RuntimeException("不能重复执行保存前联动计算");
		}
		getBEContext().setBeforeSaveDeterminated(true);

		IEntityRTDtmAssembler ass = getBeforeSaveDtmAssembler();
		if (ass == null) {
			return;
		}
		IEntityRTDtmAssembler assembler = null;
		if(extList != null){
			assembler = new BefB4SaveDtmAssembler();
			assembler.getDeterminations().addAll(ass.getDeterminations());
			assembler.getBelongingDeterminations().addAll(ass.getBelongingDeterminations());
			assembler.getChildAssemblers().addAll(ass.getChildAssemblers());

			for(ICefEntityExtend ext:extList){
				ext.addExtDtm(assembler, BeDeterminationTypes.BeforeSave);
			}
		}else{
			assembler = ass;
		}

		checkEntityContext();
		new BefB4SaveDtmExecutor(assembler, getBEContext(), getBEContext().getCurrentTransactionalChange()).execute();
	}

	@Override
	public final void beforeSaveDeterminate(IChangeDetail change) {
		throw new RuntimeException("主节点不支持此操作");
	}

	public final boolean additionalBeforeSaveDeterminate() {
		if (getBEContext().getCurrentTemplateChange() == null) {
			return false;
		}

		IEntityRTDtmAssembler ass = getBeforeSaveDtmAssembler();
		if (ass == null) {
			return false;
		}

		IEntityRTDtmAssembler assembler = null;
		if(extList != null){
			assembler = new BefB4SaveDtmAssembler();
			assembler.getDeterminations().addAll(ass.getDeterminations());
			assembler.getBelongingDeterminations().addAll(ass.getBelongingDeterminations());
			assembler.getChildAssemblers().addAll(ass.getChildAssemblers());

			for(ICefEntityExtend ext:extList){
				ext.addExtDtm(assembler, BeDeterminationTypes.BeforeSave);
			}
		}else{
			assembler = ass;
		}

		checkEntityContext();
		new BefB4SaveAdditionalDtmExecutor(assembler, getBEContext()).execute();
		return true;
	}

	protected IEntityRTDtmAssembler createAfterQueryDtmAssembler() {
		return new BefAfterQueryDtmAssembler();
	}

	public final void afterQueryDeterminate() {
		IEntityRTDtmAssembler ass = getAfterQueryDtmAssembler();
		if (ass == null) {
			return;
		}

		if(extList != null){
			for(ICefEntityExtend ext:extList){
				ext.addExtDtm(ass, BeDeterminationTypes.AfterQuery);
			}
		}

		checkEntityContext();
		new AfterQueryDtmExecutor(ass, getBEContext(),getBEContext().getBEManagerContext().getBEManager()).execute();
	}

	private IEntityRTDtmAssembler getAfterQueryDtmAssembler() {
		if(getVersion()==0)
			return createAfterQueryDtmAssembler();
		if(getBeEntityCacheInfo().getAfterQueryDtmAssembler()!=null)
			return getBeEntityCacheInfo().getAfterQueryDtmAssembler();
		BefAfterQueryDtmAssembler afterQueryDtmAssembler = (BefAfterQueryDtmAssembler) createAfterQueryDtmAssembler();
		addAfterQueryDtm(afterQueryDtmAssembler);
		addBuiltInAfterQueryDtms(afterQueryDtmAssembler);
		getBeEntityCacheInfo().setAfterQueryDtmAssembler(afterQueryDtmAssembler);
		return afterQueryDtmAssembler;
	}

	protected void addAfterQueryDtm(BefAfterQueryDtmAssembler assembler){}

	private void addBuiltInAfterQueryDtms(BefAfterQueryDtmAssembler afterQueryDtmAssembler) {

	}

	@Override
	public  Object executeBizAction(String actionCode, Object... pars) {
		throw new RuntimeException("生成型工程不支持 executeBizAction");
	}

//  public void reverse4SaveFailed(IChangeDetail change) {
//		getBEContext().appendTempCurrentChange(change);
//		getBEContext().acceptChanges();
//	}

	public IChangeDetail buildReverseTranChanges() {
		IChangeDetail change = getBEContext().getTransactionalChange();
		if(change == null)
			return null;
		else
			return UndoChangeDetailMerger.getInstance()
				.buildUndoChange(change, getBEContext().getOriginalData(), null);
	}
  //endregion
}
